home *** CD-ROM | disk | FTP | other *** search
- /*
- tcpip.c - Handling of TCP/IP networking
- Copyright ⌐2000 Andreas Schneider
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /* This is a general purpose implementaion of TCP/IP networking
- utility routines for PalmOS that I found quite useful for my
- own programs. They are designed in a way that you can compile and
- test them under Linux. It is far from complete and only contains
- routines that I personally find useful. Feel free to add useful
- stuff, but do remember that this software is distributed under
- the GNU GPL licence. Any improvements you make to this code
- must therefore be made available as source code. Why not send
- a patch file or something like that to andreas@riverrun.co.uk?
- */
-
- #ifdef linux
- // includes and defines to compile with Linux
- #include <stdio.h>
- #include <string.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #include <netdb.h>
- #include <unistd.h>
- #include <errno.h>
- #include "tcpip.h" // Stuff for this file
- #else
- // The includes for PalmOS
- #include <PalmOs.h>
- #include <sys_socket.h> // Use Berkeley sockets headers, not NetLib
- #include <unix_stdarg.h>
- #include "tcpip.h" // Stuff for this file
- #endif
-
- static const char CR = '\r';
- static const char LF = '\n';
-
- static tcpipStatusCallback status_callback=NULL;
- static tcpipErrorCallback error_callback=NULL;
- static tcpipDebugCallback debug_callback=NULL;
- // first the functions for callbacks
- extern void TcpipSetStatusCallback(tcpipStatusCallback function)
- {
- status_callback=function;
- return;
- }
-
- extern void TcpipSetErrorCallback(tcpipErrorCallback function)
- {
- error_callback=function;
- return;
- }
-
- extern void TcpipSetDebugCallback(tcpipDebugCallback function)
- {
- debug_callback=function;
- return;
- }
-
- // now 3 functions with safeguards
- #if 0 // not needed yet
- static void PrintStatus(char *message)
- {
- if (status_callback)
- {
- (*status_callback)(message);
- }
- return;
- }
- #endif
-
- static void PrintError(char *error,char *more)
- {
- if (error_callback)
- {
- (*error_callback)(error,more);
- }
- }
-
- static void PrintDebug(char *format,...)
- {
- va_list args;
-
- if (debug_callback)
- {
- va_start(args,format);
- (*debug_callback)(format,args);
- va_end(args);
- }
- return;
- }
-
- // first a few utility functions:
-
- // Converts a service name into a port number
- static int ServiceToPort(char *service,char *protocol)
- {
- int port=-1;
- struct servent *serv=NULL;
-
- serv=getservbyname(service,protocol);
- if (serv!=NULL)
- {
- port=serv->s_port;
- }
- #ifndef linux
- // in PalmOS http is not a valid service name
- // we must therefore handle that case ourselves
- // maybe I should add afew more here or even better
- // have a SetService(string,port) function? Later...
- if (StrCompare(service,"http")==0)
- {
- port=80;
- }
- #endif
- return port;
- }
-
- // Converts ascii text to in_addr struct.
- // NULL is returned if the address can not be found.
- static struct in_addr *StrToAddr(char *address)
- {
- struct hostent *host;
- static struct in_addr saddr;
-
- // First try it as aaa.bbb.ccc.ddd.
- saddr.s_addr = inet_addr(address);
- if (saddr.s_addr != -1)
- {
- PrintDebug("Address was an IP\n");
- return &saddr;
- }
- // that didn't work - look it up
- host = gethostbyname(address);
- if (host != NULL)
- {
- PrintDebug("Address was a hostname\n");
- return (struct in_addr *) *host->h_addr_list;
- }
- // didn't work either - probably doesn't exist
- PrintDebug("Address couldn't be resolved\n");
- return NULL;
- }
-
- // Make a connection to a given server/protocol/service.
- // The function returns a socket.
- // It's basically the usual Berkeley socket stuff
- // buffer is an optional buffer to buffer received data in a buffer :-)
- extern Socket MakeConnection(char *service,char *protocol,char *host_address,
- char *buffer,size_t buffer_len)
- {
- Socket sock={0};
- struct in_addr *addr;
- int connected;
- struct sockaddr_in address;
- int port=-1;
-
- // initialize the socket field to 'no socket'
- sock.socket=-1;
- // first convert the hostname/IP to a struct in_addr
- if((addr = StrToAddr(host_address))==NULL)
- {
- PrintError("MakeConnection: Invalid host address", host_address);
- return sock;
- }
- // now convert the service name to a port number
- if((port=ServiceToPort(service,protocol))<0)
- {
- PrintError("MakeConnection: Invalid service name",service);
- return sock;
- }
- // now set up the structures for the socket() call
- memset((char *) &address, 0, sizeof(address));
- address.sin_family = AF_INET;
- address.sin_port = port;
- address.sin_addr.s_addr = addr->s_addr;
- sock.buffer=buffer;
- sock.buffer_len=buffer_len;
- sock.buffer_content=0; // initially nothing in buffer
- sock.buffer_index=0;
- sock.socket = socket(AF_INET, SOCK_STREAM, 0);
- if (sock.socket==-1)
- {
- PrintError("Call to socket() failed",NULL);
- return sock;
- }
- PrintDebug("Socket is %i\n",sock.socket);
- PrintDebug("Port is %i\n",port);
- connected = connect(sock.socket, (struct sockaddr *) &address,sizeof(address));
- if (connected < 0)
- {
- sock.socket=-1;
- PrintDebug("connect failed: %i\n",errno);
- PrintError("connect", NULL);
- return sock;
- }
- return sock;
- }
-
- // send num_bytes of buffer over socket sock
- // might send it in smaller chuncks if necessary
- extern int SocketWrite(Socket *sock, char *buffer, unsigned int num_bytes)
- {
- unsigned int num_left;
- int chunk;
- int num_written;
-
- num_left = num_bytes;
- while (num_left > 0)
- {
- if (num_left > 0x7000) // limit amount to send
- {
- chunk = 0x7000; // too big
- }
- else
- {
- chunk = num_left; // can send all
- }
- num_written = write(sock->socket, buffer, chunk);
- if (num_written <= 0)
- {
- //error
- return(num_written);
- }
- num_left -= num_written;
- buffer += num_written;
- }
- return(num_bytes - num_left); // hopefully == num_bytes
- }
-
- extern int SocketReadByte(Socket *sock, char *byte)
- {
- int bytes_read=0;
- if (sock->buffer==NULL)
- {
- // the easy case - no buffer, read 1 directly from the socket
- bytes_read=read(sock->socket,byte,1);
- }
- else
- {
- // bit more tricky, but hopefully faster...
- if (sock->buffer_index>=sock->buffer_content)
- {
- // buffer_index points beyond end of buffer content
- // get more data
- bytes_read=read(sock->socket,sock->buffer,sock->buffer_len);
- PrintDebug("[%u]",bytes_read);
- if (bytes_read<=0)
- {
- // something went wrong
- return bytes_read;
- }
- // we got more data (at least one byte)
- sock->buffer_content=bytes_read;
- sock->buffer_index=0;
- }
- // now we can return a byte
- *byte=sock->buffer[sock->buffer_index];
- sock->buffer_index++;
- bytes_read=1;
- }
- return bytes_read;
- }
-
- // This function reads from a socket (a character at time) until it recieves
- // a linefeed character. It fills the 'buffer' up to the maximum size 'count'-1
- // Carriage returns and linefeeds are not included in the line.
- // A trailing \0 will be added to the line.
- // This function will return -1 if the socket is closed during the read.
- // Note that if a single line exceeds the length of count, the extra data
- // will be read but discarded.
- extern int SocketReadLine(Socket *sock, char *buffer, size_t buffer_size)
- {
- int bytes_read;
- int total_count = 0;
- char *current_position;
- char char_read = 0;
-
- // start at the beginning of the buffer
- current_position = buffer;
- // loop done at least once
- while (char_read!=LF) // wait for \n or \r\n
- {
- // read a single character from the socket
- bytes_read = SocketReadByte(sock, &char_read);
- // check for error
- if (bytes_read < 0)
- {
- // < 0 is an error code
- PrintDebug("SocketReadLine error #%i\n",bytes_read);
- return bytes_read;
- }
- else if (bytes_read==0)
- {
- // this happens if the connection was closed by the other side
- // we return -1 to indicate a problem. We can't return 0 because
- // that's what we return when we read an empty line
- PrintDebug("SocketReadLine: connection closed\n");
- return -1;
- }
- // check if we want to include the character in the line
- if ((total_count < buffer_size-1) && // only if line not too long
- (char_read!=LF) && (char_read!=CR)) // and no CR/LFs
- {
- // we want this character - store it
- *current_position = char_read;
- // PrintDebug("%c",char_read);
- current_position++;
- total_count++;
- }
- else
- {
- // well, nothing else - excess data and CR/LFs are discarded
- }
- }
- // end of loop - we must have come across the end of the line
- // now terminate the buffer with a \0
- // PrintDebug("[EOL]");
- *current_position = 0;
- return total_count;
- }
-
- extern void SocketClose(Socket *sock)
- {
- // does what you think it does
- if (sock->socket>0)
- {
- close(sock->socket);
- sock->socket=-1;
- }
- return;
- }
-